<template>
  <div>
    <h2 :class="cursorStatus">{{ answer }}</h2>
  </div>
</template>

<script setup>
import { fetchEventSource } from "@microsoft/fetch-event-source";
import { onMounted, reactive, ref, defineEmits } from "vue";

const cursorStatus = ref([]);
const answer = ref("");

/**
 * @Author: 李中华
 * @Date: 2024-01-11 14:10:15
 * @name: handleRes
 * @Descripttion: 处理返回结果
 * @param {*} code 返回状态码
 * @param {*} chat 本次对话对象
 * @param {*} data 返回数据
 * @param {*} controller 网络传输控制器
 * @return {*}
 */
const handleRes = (code, chat, data, controller) => {
  console.log("data-----", data);
  let isFirst = true;
  const resMap = {
    "-1": () => {}, // 确认还在连接的返回码
    200: () => {
      printText(chat, "", "end");
      console.log("回答结束");
      if (chatId.value == -1) {
        chatId.value = data.id;
      }
    }, // 全部传输完成返回码
    206: () => {
      printText(chat, isFirst ? data.delta.trimStart() : data.delta, "loading");
      isFirst = false;
    }, // 正在传输的返回码
    403: () => {
      printText(chat, "");
      ElMessage.error("token过期，请重新登录");
      setInputStatus(false);
      openLogin();
    }, // token过期，返回403
    421: () => {
      printText(chat, "我不会这个问题，请问我点别的吧。");
    },
    422: () => {
      printText(chat, "");
      console.log("参数错误");
    },
    423: () => {
      printText(chat, (isFirst && "\n") + "服务器忙碌中...");
    },
    503: () => {
      printText(chat, (isFirst && "\n") + "模型无法处理该请求!");
    },
  };
  // 检查对象是否包含指定的键
  if (resMap.hasOwnProperty(code)) {
    // 如果存在，执行处理函数
    const handler = resMap[code];
    handler();
  }
  if (code != 206 && code != -1) {
    controller.abort();
  }
};

let timer;
/**
 * @Author: 李中华
 * @Date: 2024-01-11 14:05:24
 * @name: getAnswer
 * @Descripttion: 根据问题获取回答
 * @param {*} chat
 * @return {*}
 */
const getAnswer = () => {
  // 控制器对象，可以根据需要中止一个或多个 Web 请求
  const controller = new AbortController();
  const { signal } = controller;
  let url = '/api/chat'
  try {
    fetchEventSource(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      signal,
      timeout: 10000, // 设置超时时间，单位是毫秒
      body: JSON.stringify({
      }),
      // 当连接打开时的回调函数
      async onopen(response) {
        console.log("连接打开时的回调函数:", response);
        if (response.status == 200) return;
      },
      // 当接收到消息时的回调函数
      onmessage(msg) {

        console.log("流式传输msg:", msg);
        const parseData = JSON.parse(msg.data);
        
        answer.value+= parseData.data;
        setCursorStatus('typing');    
        clearTimeout(timer);
        timer = setTimeout(() => {
            setCursorStatus('loading')
        }, 200)

        
      },
      // 当连接关闭时的回调函数
      onclose() {
        console.log("连接关闭");
        setCursorStatus('end')
        clearTimeout(timer);

        controller.abort();
      },
      // 当发生错误时的回调函数
      onerror(error) {
        console.log("流式传输错误:", error);
        controller.abort();
        // 必须抛出错误才会停止
        throw error;
      },
    });
  } catch (e) {
    console.log("出错", e);
  }
};

/**
 * 方法名：printText
 * 创建时间：2023/11/4
 * 作者: 李中华
 * 功能：打印回复的内容
 * @param {HTMLElement} chat - 打印内容的dom
 * @param {string} content - 打印文本内容
 * @param {"loading"|"typing"|"end"} status - 打印状态
 * @param {number} speed - 打印速度
 */
function printText(chat, content, status = "end", speed = 0) {
  // 设置鼠标状态
  setCursorStatus(status);
  // 打印内容
  chat.answer.value += content;
  // 滚动到底部
  scrollToBottom();
  // 如果所有内容打印结束，则启用输入框
  if (status === "end") {
    setCursorStatus("end");
    // 启用输入框
    disabledInput.value = false;
  }
}

/**
 * 方法名：setCursorStatus
 * 创建时间：2023/11/4
 * 作者: 李中华
 * 功能：设置dom的光标状态
 * @param {"loading"|"typing"|"end"} status - 打印状态
 */
function setCursorStatus(status) {
  const classList = {
    loading: "typing blinker",
    typing: "typing",
    end: "",
  };
  cursorStatus.value = classList[status];
}

onMounted(() => {
    getAnswer();
});
</script>

<style scoped>
* {
  box-sizing: border-box;
  line-height: normal;
}
#ai-box {
  /* 绝对定位 水平垂直居中 */
  position: absolute;
  top: 20px;
  left: 80px;
  background-color: white;
  width: 40px;
  height: 40px;
  padding: 0px;
  /* padding-left: 5px; */
  border-radius: 8px;
  display: flex;
  /* justify-content: center; */
  align-items: center;
  cursor: pointer;
  /* 动画过渡 */
  transition: 0.4s;
  /* overflow: hidden; */
}

.icon_aiview {
  padding: 6px;
}
.icon_aiview:hover {
  border-radius: 8px;
  background-color: #e2e2e2;
}

#input-box {
  display: flex;
  align-items: center;
  overflow: hidden;
  width: 0px;
  /* position: absolute;
      left: 0;
      top: 0; */
  background: #fff;
}

#input-txt {
  /* margin-left: 10px; */
  border: none;
  background: none;
  outline: none;
  padding-left: 10px;
  color: #000000;
  font-size: 16px;
  height: 30px;
  width: 500px;
  border-left: 1px solid #878787;
}

#dialog {
  position: absolute;
  left: 80px;
  top: 75px;
  width: 620px;
  height: 473px;
  background: #fff;
  border-radius: 8px;
}

#chat-container {
  display: flex;
  flex-wrap: nowrap;
  position: absolute;
  bottom: 40px;
  width: 618px;
  height: 393px;
  overflow-y: auto;
  /* align-items: flex-start; */
  flex-direction: column;
}

/* 隐藏滚动条 */
#chat-container::-webkit-scrollbar {
  display: none;
}

.avatar {
  width: 36px;
  height: 36px;
  background: #005ce6;
  border-radius: 8px;
  color: #fff;
  font-weight: 500;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-right: 30px;
}

.question {
  display: flex;
  width: 620px;
  padding: 10px;
}

div[type] {
  width: 540px;
  /* white-space:'pre'; */
}

.answer {
  display: flex;
  width: 620px;
  /* height: 321px; */
  padding: 10px;
  background: #f7f7f8;
  border: 1px solid #cacccb;
}

/*  光标字符显示 */
.typing::after {
  content: "▌";
}
/* 光标闪烁动画 */
.blinker::after {
  animation: blinker 1s step-end infinite;
}
@keyframes blinker {
  0% {
    visibility: visible;
  }
  50% {
    visibility: hidden;
  }
  100% {
    visibility: visible;
  }
}
</style>
